#include "../ocl/devmap.h"
#include "../mds/sys/string.h"


 
static
int ocl_devmap_parse( ocl_devmap *devmap, const ocl_driver *driver, const char *text )
{
	static const char isep[]   = " \t\r\n";
	static const char wsep[]   = ":";
	static const char fn[]     = "[ocl_devmap_parse]";
	assert( devmap );
	assert( driver );
	assert( text != NULL );
	assert( devmap->devices != NULL );
	assert( devmap->num_devices == 0 );
	{
		/* copy */
		char *input = mds_string_copy(text);
		char *ibrk  = NULL;
		char *word  = NULL;
		if( !input ) return -1;
		
		/* loop over the identifiers */
		for(word = mds_strtok_r(input, isep, &ibrk);
			word != NULL;
			word = mds_strtok_r(NULL, isep, &ibrk))
		{
			char   *wbrk   = NULL;
			char   *p_text = NULL;
			char   *d_text = NULL;
			cl_uint p_indx = 0;
			cl_uint d_indx = 0;
			if( ocl_logfile ) fprintf( ocl_logfile, "%s found '%s'\n", fn, word );
			if( devmap->num_devices >= driver->num_devices )
			{
				errno = ERANGE;
				goto ERROR;
			}
			
			/* find platform text */
			p_text = mds_strtok_r(word, wsep, &wbrk );
			if( !p_text )
			{
				errno = EINVAL;
				goto ERROR;
			}
			if( ocl_logfile ) fprintf( ocl_logfile,"%s \t platform= '%s'\n", fn, p_text );
			
			/* find device text */
			d_text = mds_strtok_r(NULL, wsep, &wbrk );
			if( !d_text )
			{
				errno = EINVAL;
				goto ERROR;
			}
			if( ocl_logfile ) fprintf( ocl_logfile,"%s \t device  = '%s'\n", fn, d_text );
			
			/* and no other is allowed */
			if( mds_strtok_r(NULL, wsep, &wbrk ) )
			{
				errno = EINVAL;
				goto ERROR;
			}
			
			/* converting */
			errno = 0;
			p_indx = strtol(p_text, NULL, 10);
			if( errno ) goto ERROR;
			d_indx =  strtol(d_text, NULL, 10);
			if( errno ) goto ERROR;
			if( ocl_logfile ) fprintf( ocl_logfile, "%s \t\t maps to '%u:%u'\n", fn, p_indx, d_indx);
			
			/* checking ! */
			if( p_indx >= driver->num_platforms )
			{
				errno = ERANGE;
				goto ERROR;
			}
			
			if( d_indx >= driver->platforms[p_indx].num_devices )
			{
				errno = ERANGE;
				goto ERROR;
			}
			
			/* registering */
			assert( devmap->num_devices < driver->num_devices );
			devmap->devices[ devmap->num_devices++ ] = driver->platforms[p_indx].devices_id[ d_indx ];
			
		}
		
		if( devmap->num_devices <= 0 )
		{
			errno = EINVAL;
			goto ERROR;
		}
		
		mds_string_delete(input);
		return 0;
		
	ERROR:
		mds_string_delete(input);
		return -1;
	}
	
}

ocl_devmap * ocl_devmap_create( const ocl_driver *driver, const char *text )
{
	assert( driver );
	assert( driver->num_devices > 0 );
	ocl_errno = CL_SUCCESS;
	{
		/****************************/
		/* allocate maximum memory */
		/**************************/
		const size_t bytes = sizeof(ocl_devmap) + driver->num_devices * sizeof(cl_device_id);
		const char  *array = MDS_CALLOC_AS(char,bytes);
		if( !array ) 
			return NULL;
		else
		{
			/****************************/
			/* populate devmap         */
			/************************(*/
			ocl_devmap *devmap = (ocl_devmap *)   & array[0];
			devmap->devices    = (cl_device_id *) & array[ sizeof(ocl_devmap) ];
			devmap->reserved   = bytes;
			assert( devmap->num_devices == 0 );
			if( NULL == text )
			{
				/****************************/
				/* with everyone           */
				/**************************/
				cl_uint          idx  = 0;
				register cl_uint i;
				devmap->num_devices = driver->num_devices;
				for( i=0; i < driver->num_platforms; ++i )
				{
					ocl_platform *P = & driver->platforms[i];
					register cl_uint j;
					for(  j=0; j < P->num_devices; ++j )
					{
						devmap->devices[idx++] = P->devices_id[j];
					}
				}
			}
			else 
			{
				/****************************/
				/* simple parsing          */
				/**************************/
				if( ocl_devmap_parse(devmap, driver, text) < 0 )
				{
					ocl_devmap_delete( devmap );
					return NULL;
				}
			}
			
			return devmap;
		}
	}
	
}

void ocl_devmap_delete( ocl_devmap *devmap )
{
	if( devmap )
	{
		assert( devmap->reserved > 0 );
		MDS_FREE_AS(char,devmap,devmap->reserved);
	}
}


int          ocl_context_map(ocl_context        *context, 
							 const ocl_devmap   *devmap)
{
	assert( context );
	assert( devmap  );
	assert( devmap->reserved > 0 );
	return ocl_context_load(context, devmap->num_devices, devmap->devices );
	
}

void ocl_devmap_write( const ocl_devmap *devmap, const ocl_driver *driver, FILE *fp )
{
	register cl_uint i;
	assert( devmap );
	assert( devmap->reserved > 0 );
	assert( driver );
	fprintf(fp, "######## OpenCL devices ########\n");
	for( i=0; i < devmap->num_devices; ++i )
	{
		const cl_device_id d = devmap->devices[ i ];
		const ocl_device  *D = ocl_driver_device( d );
		if( !D )
			fprintf( fp, "\tInvalid cl_device_id!!\n");
		else {
			fprintf( fp, "\t\t(*) '%s'\n", D->NAME );
		}
	}
}

